#ifndef __MEMLINE_DOT_HPP__
#define __MEMLINE_DOT_HPP__
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
#include <iomanip>
#include <stdint.h>
#include <assert.h>
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
#define SETFIX( w, p )   std::fixed      << std::setprecision( p ) << std::setw( w ) << std::setfill(' ')
#define SETSCI( w )      std::scientific << std::setprecision( w )
#define SETDEC( w )   std::dec   << std::setw( w ) << std::setfill(' ')
#define SETHEX( w )   std::hex   << std::setw( w ) << std::setfill('0')
#define ZERODEC( w )  std::dec   << std::setw( w ) << std::setfill('0')
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
const uint32_t lineSize  =     64;
const uint32_t pageSize  = 4*1024;
const uint32_t hugeSize  = 2*1024*1024;
const uint32_t wordSize  = sizeof( uint64_t );
const uint32_t wordsPerLine = lineSize / wordSize;
const uint32_t wordsPerPage = pageSize / wordSize;
const uint32_t wordsPerHuge = hugeSize / wordSize;
const uint32_t linesPerPage = pageSize / lineSize;
const uint32_t linesPerHuge = hugeSize / lineSize;
const uint32_t pagesPerHuge = hugeSize / pageSize;
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
uint64_t clog2( uint64_t v );
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
class line_t {
public:
    uint64_t d[ wordsPerLine ];
    line_t( void ) {}
    line_t ( const line_t & l ) {
        d[0] = l.d[0];
        d[1] = l.d[1];
        d[2] = l.d[2];
        d[3] = l.d[3];
        d[4] = l.d[4];
        d[5] = l.d[5];
        d[6] = l.d[6];
        d[7] = l.d[7];
    }
    line_t ( const line_t * l ) {
        d[0] = l->d[0];
        d[1] = l->d[1];
        d[2] = l->d[2];
        d[3] = l->d[3];
        d[4] = l->d[4];
        d[5] = l->d[5];
        d[6] = l->d[6];
        d[7] = l->d[7];        
    }
    inline const uint64_t * RawPtr( void ) const { return &d[0]; }
    inline unsigned char * RawUCharPtr( void ) const {
        unsigned char * p = ( unsigned char * ) &( d[0] );
        return p;
    }
    inline char * RawCharPtr( void ) const {
        char * p = ( char * ) &( d[0] );
        return p;
    }
    inline const bool operator <  ( const line_t & other ) const {
       if( d[7]  < other.d[7] ) { return true; }
       if( d[7] == other.d[7] ) {
          if( d[6]  < other.d[6] ) { return true; }
          if( d[6] == other.d[6] ) {
             if( d[5]  < other.d[5] ) { return true; }
             if( d[5] == other.d[5] ) {
                if( d[4]  < other.d[4] ) { return true; }
                if( d[4] == other.d[4] ) {
                   if( d[3]  < other.d[3] ) { return true; }
                   if( d[3] == other.d[3] ) {
                      if( d[2]  < other.d[2] ) { return true; }
                      if( d[2] == other.d[2] ) {
                         if( d[1]  < other.d[1] ) { return true; }
                         if( d[1] == other.d[1] ) {
                            if( d[0]  < other.d[0] ) {
                               return true;
                            }
                         }
                      }
                   }
                }
             }
          }
       }
       return false;
    }
    inline const bool operator != ( const line_t & other ) const {
        if( *this == other ) { return false; }
        return true;
    }
    inline const bool operator == ( const line_t & other ) const {
        if     ( other.d[0] != d[0] ) { return false; }
        else if( other.d[1] != d[1] ) { return false; }
        else if( other.d[2] != d[2] ) { return false; }
        else if( other.d[3] != d[3] ) { return false; }
        else if( other.d[4] != d[4] ) { return false; }
        else if( other.d[5] != d[5] ) { return false; }
        else if( other.d[6] != d[6] ) { return false; }
        else if( other.d[7] != d[7] ) { return false; }
        return true;
    }
    inline line_t &   operator =  ( const line_t & rhs ) {
        d[0] = rhs.d[0];
        d[1] = rhs.d[1];
        d[2] = rhs.d[2];
        d[3] = rhs.d[3];
        d[4] = rhs.d[4];
        d[5] = rhs.d[5];
        d[6] = rhs.d[6];
        d[7] = rhs.d[7];
        return *this;
    }
    inline const bool IsZero( void ) const {
       if( d[0] == 0ULL ) {
          if( d[1] == 0ULL ) {
             if( d[2] == 0ULL ) {
                if( d[3] == 0ULL ) {
                   if( d[4] == 0ULL) {
                      if( d[5] == 0ULL ) {
                         if( d[6] == 0ULL ) {
                            if( d[7] == 0ULL ) {
                               return true;
                            }
                         }
                      }
                   }
                }
             }
          }
       }
       return false;
    }
    inline const bool IsNonZero( void ) const {
        return !IsZero();
    }
    inline void SetToZero( void ) {
        d[0] = 0ULL;
        d[1] = 0ULL;
        d[2] = 0ULL;
        d[3] = 0ULL;
        d[4] = 0ULL;
        d[5] = 0ULL;
        d[6] = 0ULL;
        d[7] = 0ULL;
    }
    inline void SetWord( const uint64_t word, const uint32_t wordIdx ) {
        d[ wordIdx ] = word;
    }
    friend std::ostream & operator << ( std::ostream & os, const line_t & line );
};

class page_t {
    line_t * lines;
    const uint32_t numLines;
public:
    page_t ( const uint32_t _numLines ) : numLines( _numLines ) {
        lines = new line_t[ numLines ];
    }
    ~page_t( void ) {
        delete [] lines;
    }
    page_t ( const page_t & p ) : numLines( p.NumLines() ) {
        lines = new line_t[ numLines ];
        for( uint32_t l = 0; l < numLines; l++ ) {
            lines[ l ] = p.lines[ l ];
        }
    }
    inline const bool operator <  ( const page_t & other ) const {
        for( uint32_t l = 0; l < numLines; l++ ) {
            if( lines[ l ] < other.lines[ l ] ) { return true;  }
            if( other.lines[ l ] < lines[ l ] ) { return false; }
        }
        // apparently they are equal in value
        return false;
    }
    inline const bool operator == ( const page_t & other ) const {
        for( uint32_t l = 0; l < numLines; l++ ) {
            if( lines[ l ] != other.lines[ l ] ) { return false; }
        }
        return true;
    }
    inline const bool operator != ( const page_t & other ) const {
        for( uint32_t l = 0; l < numLines; l++ ) {
            if( lines[ l ] != other.lines[ l ] ) { return true; }
        }
        return false;
    }
    inline page_t &   operator =  ( const page_t & rhs ) {
        for( uint32_t l = 0; l < numLines; l++ ) {
            lines[ l ] = rhs.lines[ l ];
        }
        return *this;
    }
    inline const bool IsZero( void ) const {
        for( uint32_t l = 0; l < numLines; l++ ) {
            if( lines[ l ].IsNonZero() ) { return false; }
        }
        return true;
    }
    inline const bool IsNonZero( void ) const {
        if( IsZero() ) {
            return false;
        }
        return true;
    }
    inline void SetToZero( void ) {
        for( uint32_t l = 0; l < numLines; l++ ) {
            lines[ l ].SetToZero();
        }
    }
    inline void SetLine( const uint32_t lineIdx, line_t * line ) {
        assert( lineIdx < numLines );
        lines[ lineIdx ] = *line;
    }
    
    inline const uint64_t * RawPtr( void ) const {
        const uint64_t * x = &( lines[0].d[0] );
        return x;
    }
    inline const uint32_t   NumBytes( void ) const {
        return lineSize * numLines;
    }
    inline const uint32_t   NumLines( void ) const {
        return numLines;
    }
    inline const uint32_t   NumWords( void ) const {
        return wordsPerLine * numLines;
    }
    inline const line_t * LinePtr( const uint32_t l ) const {
        return &lines[l];
    }
    inline const uint64_t * FirstNonZeroRawPtr( void ) const {
        assert( IsNonZero() );
        uint32_t l = 0;
        while( LinePtr( l )->IsZero() ) { l++; }
        return LinePtr( l )->RawPtr();
    }
};

std::ostream & operator << ( std::ostream & os, const line_t & line );

#endif
